/**
  ******************************************************************************
  * @file    usart.c
  * @author  Puya Application Team
  * @brief   Contains USART HW configuration
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2021 Puya Semiconductor.
  * All rights reserved.</center></h2>
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "wdg.h"
#include "iap.h"
#include "common.h"

#define MAX_BAUDRATE            (2000000U + 100U)
#define MIN_BAUDRATE            (1200U - 100U)
#define DEFAULT_BAUDRATE        (115200U)
#define CALC_BRR(baud)          ((SYS_CLK + (baud) / 2U) / (baud))

static uint32_t gCurrBaudrate = DEFAULT_BAUDRATE;

/**
 * @brief  Initialize USART parameters.
 */
void UsartInit(void)
{
  /**
   * Usart1_TX(PA9),Usart1_RX(PA10)
   * UsartParam: initial:115200,Even,8,1
  */

  SET_BIT(RCC->IOPENR, RCC_IOPENR_GPIOAEN);
  SET_BIT(RCC->APBENR2, RCC_APBENR2_USART1EN);

  WRITE_REG(GPIOA->MODER, 0xEBFFFFFFUL);
  CLEAR_BIT(GPIOA->MODER, (GPIO_MODER_MODE9_0 | GPIO_MODER_MODE10_0));
  SET_BIT(GPIOA->AFR[1], (GPIO_AFRH_AFSEL9_0 | GPIO_AFRH_AFSEL10_0));
  CLEAR_BIT(GPIOA->PUPDR, GPIO_PUPDR_PUPD3_0 | GPIO_PUPDR_PUPD15_0);

  SET_BIT(USART1->CR1, USART_CR1_M);
  SET_BIT(USART1->CR1, USART_CR1_PCE);
  CLEAR_BIT(USART1->CR1, USART_CR1_PS);
  WRITE_REG(USART1->BRR, CALC_BRR(DEFAULT_BAUDRATE));

  SET_BIT(USART1->CR1, USART_CR1_RE);
  SET_BIT(USART1->CR1, USART_CR1_TE);
  SET_BIT(USART1->CR1, USART_CR1_UE);

  gCurrBaudrate = DEFAULT_BAUDRATE;
}

/**
 * @brief  Set USART baud rate.
 * @param  uiBaudrate  Baud rate value
 */
void UsartSetBaudrate(uint32_t uiBaudrate)
{
  /* Check if the set baud rate is the same as the current baud rate */
  if(gCurrBaudrate == uiBaudrate)
  {
    return ;
  }

  uint32_t uiBrr = CALC_BRR(uiBaudrate);

  CLEAR_BIT(USART1->CR1, USART_CR1_RE);

  while (!READ_BIT(USART1->SR, USART_SR_TC))
  {
    WdgRefresh();
  }

  (void)USART1->SR;

  WRITE_REG(USART1->BRR, uiBrr);

  SET_BIT(USART1->CR1, USART_CR1_RE);


  (void)USART1->SR;

  gCurrBaudrate = uiBaudrate;
}

/**
 * @brief  Validate baud rate parameter.
 * @param  uiBaudrate  Baud rate
 * @retval 1 if valid, 0 otherwise
 */
uint8_t ValidateBpsParam(uint32_t uiBaudrate)
{
  if ((uiBaudrate >= MIN_BAUDRATE) && (uiBaudrate <= MAX_BAUDRATE))
  {
    return SUCCESS;
  }

  return ERROR;
}

/**
 * @brief  Configure USART baud rate.
 */
void UsartConfigureBps(void)
{
  /* Receive baudrate parameter */
  uint8_t pubData[4] = { 0U };

  pubData[0] = UsartReadByte();
  pubData[1] = UsartReadByte();
  pubData[2] = UsartReadByte();
  pubData[3] = UsartReadByte();

  uint32_t uiBaudrate = (pubData[0] << 24U) | \
                        (pubData[1] << 16U) | \
                        (pubData[2] << 8U)  | \
                        (pubData[3]);

  if (UsartReadByte() != GetXOR(pubData, 0x04, 0x00))
  {
    UsartSendByte(NACK_BYTE);
    return ;
  }

  /* Check parameters */
  if (ValidateBpsParam(uiBaudrate))
  {
    UsartSendByte(NACK_BYTE);
    return ;
  }

  UsartSendByte(ACK_BYTE);

  UsartSetBaudrate(uiBaudrate);

  TimeoutInit();

  while (!TimeoutHandle(5000))
  {
    WdgRefresh();

    if( USART_SR_RXNE == READ_BIT(USART1->SR, USART_SR_RXNE) && \
        ((READ_BIT(USART1->DR, USART_DR_DR) & 0xFFU) == SYNC_BYTE))
    {
      UsartSendByte(ACK_BYTE);
      return;
    }
  }

  UsartSendByte(NACK_BYTE);
  UsartSetBaudrate(DEFAULT_BAUDRATE);
}

/**
 * @brief  Send one byte via USART.
 * @param  ubData  Byte to send
 */
void UsartSendByte(uint8_t ubData)
{
  (void)USART1->SR;

  USART1->DR = (ubData & (uint16_t)0x01FF);

  while (USART_SR_TC != READ_BIT(USART1->SR, USART_SR_TC))
  {
    WdgRefresh();
  }
}

/**
 * @brief  Read one byte from USART.
 * @retval Byte read
 */
uint8_t UsartReadByte(void)
{
  while (USART_SR_RXNE != READ_BIT(USART1->SR, USART_SR_RXNE))
  {
    WdgRefresh();
  }

  return (uint8_t)(READ_BIT(USART1->DR, USART_DR_DR) & 0xFFU);
}

#ifdef _DEBUG

int fputc(int ch, FILE *f)
{
  UsartSendByte(ch);

  return (ch);
}
#endif
